package org.deltaset.faces;

import java.io.IOException;
import java.net.URL;
import java.util.ArrayList;
import java.util.Enumeration;
import java.util.List;
import java.util.Locale;
import java.util.Map;
import java.util.Properties;
import java.util.ResourceBundle;
import java.util.logging.Level;
import java.util.logging.Logger;
import javax.el.ELContext;
import javax.el.ExpressionFactory;
import javax.el.ValueExpression;
import javax.faces.application.FacesMessage;
import javax.faces.component.UIComponent;
import javax.faces.context.FacesContext;
import org.deltaset.faces.internal.DefaultOutcomeBuilder;

/**
 * internal utility class
 * @author dmakariev
 */
public abstract class FacesUtils {

    private FacesUtils() {
    }
    
    private static final Logger logger = Logger.getLogger(FacesUtils.class.getName());

    //TODO: check if should be deprecated
    //the bundle interface that case should be registered within faces-config ??
    public static String getBundleKey(String bundleName, String key) {
        return FacesContext.getCurrentInstance().getApplication().getResourceBundle(FacesContext.getCurrentInstance(), bundleName).getString(key);
    }

    public static String getLabel(String bundleName, String key) {
        try {
            final Locale currentLocale = FacesContext.getCurrentInstance().getViewRoot().getLocale();
            final ResourceBundle rb = ResourceBundle.getBundle(bundleName, currentLocale);
            final String label = rb.getString(key);
            //final String label = FacesContext.getCurrentInstance().getApplication().getResourceBundle(FacesContext.getCurrentInstance(), bundleName).getString(key);
            return label;
        } catch (Exception ex) {
            logger.log(Level.INFO, "bundleName = {0}", bundleName);
            logger.log(Level.INFO, "bundleName = {0}", key);
            logger.log(Level.INFO, " EXCEPTION FacesUtils.getLabel ",ex);
            return null;
        }
    }

    public static void addSuccessMessage(String msg) {
        addMessage(FacesMessage.SEVERITY_INFO, msg);
    }

    public static void addErrorMessage(String msg) {
        addMessage(FacesMessage.SEVERITY_ERROR, msg);
    }

    public static boolean getHasValidationErrors() {
        final FacesContext facesContext = FacesContext.getCurrentInstance();
        return FacesMessage.SEVERITY_ERROR.equals(facesContext.getMaximumSeverity());
    }

    @Deprecated
    public static String getParameter(String name) {
        final String param = FacesContext.getCurrentInstance().getExternalContext().getRequestParameterMap().get(name);
        return null == param ? "" : param;
    }

    public static Long getParameterLong(String name) {
        final String value = FacesContext.getCurrentInstance().getExternalContext().getRequestParameterMap().get(name);
        return null != value && value.length() > 0 ? Long.valueOf(value) : null;
    }

    public static String getParameterString(String name) {
        final String value = FacesContext.getCurrentInstance().getExternalContext().getRequestParameterMap().get(name);
        return null != value && value.length() > 0 ? value : null;
    }

    public static String getHeader(String name) {
        final String param = FacesContext.getCurrentInstance().getExternalContext().getRequestHeaderMap().get(name);
        return null == param ? "" : param;
    }

    private static void addMessage(FacesMessage.Severity severity, String msg) {
        final FacesMessage facesMsg = new FacesMessage(severity, msg, msg);
        FacesContext.getCurrentInstance().addMessage(null, facesMsg);
    }

    public static OutcomeBuilder outcomeBuilder(String outcome) {
        return new DefaultOutcomeBuilder(outcome);
    }

    public static <T> T getManagedBean(String beanName, Class<T> clazz) {
        final FacesContext fc = FacesContext.getCurrentInstance();
        final ELContext elc = fc.getELContext();
        final ExpressionFactory ef = fc.getApplication().getExpressionFactory();
        final String valueExpressionString = prepareValueExpression(beanName);
        final ValueExpression ve = ef.createValueExpression(elc, valueExpressionString, clazz);
        return (T) ve.getValue(elc);
    }

//    public static <T> void viewScopePut(final String name, final T value) {       
//        getViewScope().put(name, value);
//    }
//    
//    public static <T> T viewScopeGet(final String name){
//        return (T) getViewScope().get(name);
//    }
    public static Map<String, Object> getSessionMap() {
        final FacesContext fc = FacesContext.getCurrentInstance();
        final Map<String, Object> sessionScope = fc.getExternalContext().getSessionMap();
        return sessionScope;
    }

    public static Map<String, Object> getViewScope() {
        final FacesContext fc = FacesContext.getCurrentInstance();
        final Map<String, Object> viewScope = fc.getViewRoot().getViewMap(true);
        return viewScope;
    }

    public static <T> T cachedInViewScope(final String name, final T value) {
        final Map<String, Object> viewScope = getViewScope();

        if (viewScope.containsKey(name)) {
            final Object cached = viewScope.get(name);         
            logger.log(Level.INFO, "cached = {0}", cached);
            return (T) cached;
        }       
        logger.log(Level.INFO, "CACHING... = {0}", value);
        return (T) viewScope.put(name, value);
    }

    public static void setValueExpression(String expression, Object value) {
        final FacesContext fc = FacesContext.getCurrentInstance();
        final ELContext elc = fc.getELContext();
        final ExpressionFactory ef = fc.getApplication().getExpressionFactory();
        final String valueExpressionString = prepareValueExpression(expression);
        final ValueExpression ve = ef.createValueExpression(elc, valueExpressionString, Object.class);
        ve.setValue(elc, value);
    }

    private static String prepareValueExpression(String probableValueExpressionString) {
        final String valueExpressionString = probableValueExpressionString.startsWith("#{") && probableValueExpressionString.endsWith("}") ? probableValueExpressionString : "#{" + probableValueExpressionString + "}";
        return valueExpressionString;
    }

    //TODO: just temporary .. should move to module infrastructure..
    public static String[] loadThemes() {
        final String resourceName = "META-INF/deltaset-theme-provider.properties";
        final Properties p = new Properties();
        final List<String> result = new ArrayList<String>();
        try {

            final ClassLoader classLoader = FacesUtils.class.getClassLoader();
            final Enumeration<URL> e = classLoader.getResources(resourceName);
            int count = 0;

            //TODO: think about error when the theme is already added interface the reslut list....
            while (e.hasMoreElements()) {
                count++;
                URL url = e.nextElement();
                p.load(url.openStream());
                final String multipleThemeIds = p.getProperty("themeIdAll");
                if (null != multipleThemeIds) {
                    final String[] themeIds = multipleThemeIds.split(",");
                    for (String themeId : themeIds) {
                        if (!result.contains(themeId)) {
                            result.add(themeId);
                        }
                    }
                }
                final String singleThemeId = p.getProperty("themeId");
                if (null != singleThemeId) {
                    if (!result.contains(singleThemeId)) {
                        result.add(singleThemeId);
                    }
                }
            }
        } catch (IOException ex) {
            throw new RuntimeException("Cannot load theme descriptor resource[" + resourceName + "]", ex);
        }
        final String[] resultArray = new String[result.size()];
        result.toArray(resultArray);
        return resultArray;
    }
    
    public static String getAttribute(final UIComponent uic, final String attributeName){
        final Map<String, Object> attrs = uic.getAttributes();
        final String attributeValue = (String) attrs.get(attributeName);
        return attributeValue;
    }
    
    public static <T> T getAttribute(final UIComponent uic, final String attributeName, final Class<T> clazz){
         try {
            final ValueExpression valueVE = uic.getValueExpression(attributeName);
            final ELContext currentELContext = FacesContext.getCurrentInstance().getELContext();
            final T attributeValue = (T) valueVE.getValue(currentELContext);
            return attributeValue;
        } catch (NullPointerException ex) {
            throw new RuntimeException(uic.getClass().getName()+ " not configured correctly ! missing "+attributeName, ex);
        } catch (ClassCastException ex) {
            throw new RuntimeException(uic.getClass().getName() + " not configured correctly ! wrong "+attributeName, ex);
        }
    }
}
